home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Atari Compendium
/
The Atari Compendium (Toad Computers) (1994).iso
/
files
/
umich
/
network
/
midi
/
mx2net20.lzh
/
MX2NET20
/
NETWORK.MOD
< prev
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
Macintosh to JP
NeXTSTEP
RISC OS/Acorn
Shift JIS
UTF-8
Wrap
Modula Implementation
|
1990-12-15
|
38.4 KB
|
1,178 lines
(* *)
(* Copyright 1989 fred brooks LogicTek *)
(* *)
(* First Release 12/8/87-FGB *)
(* Added drive config data routines, clean up VBL code, *)
(* Remote time option 12/17/89-FGB *)
(* *)
IMPLEMENTATION MODULE NETWORK ;
(* --------------------------------------------------------------------------
NETWORK : TWO CPU NETWORK FOR TDI Modula-2/ST
--------------------------------------------------------------------------*)
(*$T-,$S-,$A+ *)
FROM SYSTEM IMPORT ADDRESS, ADR, SETREG, CODE, REGISTER ,BYTE ,TSIZE;
FROM BIOS IMPORT BPB ,BConStat ,BConIn, BCosStat, BConOut, Device,
MediaChange,MCState,GetBPB,RWAbs,RW,DriveSet,DriveMap;
FROM XBIOS IMPORT SuperExec,IORec,IORECPTR,IOREC,SerialDevice,
GetDateTime,ScreenPhysicalBase;
FROM GEMDOS IMPORT TermRes,Open,Close,SetDate,SetTime ;
IMPORT GEMDOS;
FROM ASCII IMPORT SYN,STX,SOH,BEL,CR,LF,ESC;
CONST
MaxSeq = 1;
Maxdrives = 31; (* number of disk drives minus 1 *)
recsize = 511;
retry = 5;
MAGIC = 314159;
Memdrive = 31;
chanwait = 3;
debug = FALSE;
trace = FALSE;
TITLE = "MX2NET Version 2.0 Fred Brooks UUCP crash!fgbrooks";
(* Because we dont know what registers the BIOS is using we must use
the following opcodes to save the registers *)
MOVEMDEC = 48E7H ; (* 68000 opcode for MOVEM <regs>,-(A7) *)
MOVEMINC = 4CDFH ; (* 68000 opcode for MOVEM (A7)+,<regs> *)
SAVEREGS = 07FFCH ; (* Registers D1..A5 for DEC *)
RESTREGS = 03FFEH ; (* Registers D1..A5 for INC *)
RTS = 04E75H ; (* 68000 return from subroutine opcode *)
TYPE
(* Procedure types to mimic correct sequence for "C" BIOS routines *)
CBPBProc = PROCEDURE ( CARDINAL ) ;
CMediaChProc = PROCEDURE ( CARDINAL ) ;
CRWAbsProc = PROCEDURE ( CARDINAL, CARDINAL, CARDINAL, ADDRESS, CARDINAL );
MIDIbuffer = ARRAY [0..1023] OF CARDINAL;
SequenceNr = [0..MaxSeq];
message = ARRAY [0..recsize] OF BYTE;
message1 = ARRAY [0..17] OF BYTE;
FrameKind = (ack,data,callreq,callaccp,clearreq,clearconf,
resetreq,diag);
DataKind = (rdmediareq,rdmediaconf,rdbpbreq,rdbpbconf,
rdrwabsreq,rdrwabsconf,memreq,memconf,timereq,timeconf);
evtype = (framearrival,cksumerr,timeout,hostready,reset,nothing);
channel = (none,local,remote);
frame = RECORD
syn : CHAR; (* these are sync chars *)
stx : CHAR; (* for the frames *)
kind : FrameKind;
seq : SequenceNr;
ack : SequenceNr;
cmd : DataKind;
rw : CARDINAL; (* read or write data *)
recno : CARDINAL; (* sector for data*)
d0 : LONGCARD; (* data return variable *)
info : message;
cksum : CARDINAL;
END;
framecptr = POINTER TO framecmd;
framecmd = RECORD
syn : CHAR; (* these are sync chars *)
stx : CHAR; (* for the frames *)
kind : FrameKind;
seq : SequenceNr;
ack : SequenceNr;
cmd : DataKind;
rw : CARDINAL; (* read or write data *)
recno : CARDINAL; (* sector for data*)
d0 : LONGCARD; (* data return variable *)
info : message1;
cksum : CARDINAL;
END;
control = RECORD
magic : LONGCARD;
reset : BOOLEAN;
networkactive : BOOLEAN;
remotedrive : CARDINAL;
drivemap : DriveSet;
nextframetosend : SequenceNr;
frameexpected : SequenceNr;
sendreset : BOOLEAN;
END;
netmap = RECORD
Remote : CARDINAL;
Local : CARDINAL;
Write : BOOLEAN;
END;
frameptr = POINTER TO ARRAY [0..1024] OF BYTE;
VAR
(* BIOS variables : These can only be accessed with the 68000 in supervisor
mode. The Modula-2 language allows you to fix the location of variables *)
HDBPB [0472H] : ADDRESS ; (* hard disk get Bios Parameter Block *)
HDRWAbs [0476H] : ADDRESS ; (* hard disk read/write abs *)
HDMediaCh [047EH] : ADDRESS ; (* hard disk media change *)
EvtCritic [0404H] : ADDRESS ; (* evt_critic *)
DriveBits [04C2H] : SET OF [0..31]; (* disk drives present map *)
flock [043EH] : LONGCARD; (* disk access in progress *)
hz200 [04baH] : LONGCARD; (* 200hz clock counter *)
NetBits : SET OF [0..31];
Dptr : DriveSet; (* save original drive map *)
Mptr : LONGCARD;
charcount,framesize,cksum,recframesize,sndframesize,
SIZEframe,SIZEframecmd : CARDINAL;
networkconnect : BOOLEAN; (* DCD = 1 TRUE *)
gotframe : BOOLEAN;
framebufferfull : BOOLEAN;
cleartosend : BOOLEAN;
readytosend : BOOLEAN;
requesttosend : BOOLEAN;
framewaiting : BOOLEAN;
OK,installed : BOOLEAN;
gotmediach : ARRAY [0..Maxdrives] OF BOOLEAN;
gotbpb : ARRAY [0..Maxdrives] OF BOOLEAN;
networkerror : BOOLEAN;
shortframe : BOOLEAN;
vblLock : BOOLEAN;
rwabsLock : BOOLEAN;
TIMESET : BOOLEAN;
OneTime : BOOLEAN;
ChannelLock : channel;
NetMap : ARRAY [0..Maxdrives] OF netmap;
NetInfo : ARRAY [0..128] OF CHAR;
statptr : POINTER TO stat;
sframe,rframe,SFRAME,RFRAME : frame;
rframeptr : frameptr;
framecmdptr : framecptr;
sframecmdptr : framecptr;
event : evtype;
C : control;
S : stat;
recchar : LONGCARD;
result,i,i1,mediacount,handle : INTEGER;
D0ptr : POINTER TO LONGCARD;
wsector,drvnr,d,R : CARDINAL;
rbuffer : MIDIbuffer;
rbptr,kbdiorec : IORECPTR;
numBytes,sec,min,hour,time,count : LONGCARD ;
status : LONGINT ;
sframeptr : frameptr;
(* The following are saved copies of the BIOS variables so that the real
hard disk routines can be called if a hard disk access is requested. *)
SaveHDBPB : CBPBProc ; (* hard disk get Bios Parameter Block *)
SaveHDRWAbs : CRWAbsProc ; (* hard disk read/write abs *)
SaveHDMediaCh : CMediaChProc ; (* hard disk media change *)
SaveCritic : PROC;
(* NETWORK control *)
NetworkBPB : ARRAY [0..Maxdrives] OF BPB ; (* BIOS Parameter block for NETWORK *)
PROCEDURE inc(VAR k: SequenceNr); (* increment k circulary *)
BEGIN
IF k<MaxSeq THEN INC(k) ELSE k:=0 END;
END inc;
MODULE NETBIOS;
IMPORT getfromremote,frameptr,ADDRESS,CODE,MOVEMDEC,SAVEREGS,status,
Memdrive,statptr,S,MOVEMINC,RESTREGS,NetBits,wsector,DataKind,
C,NetMap,resetnewdisk,channel,ADR,rwabsLock,frame,
networkerror,SETREG,SaveHDBPB,SaveHDRWAbs,SaveHDMediaCh,TSIZE,
newdisk,gotbpb,gotmediach,MCState,NetworkBPB,BPB;
EXPORT RDRWAbs,RDMediaCh,RDBPB;
VAR i3 : CARDINAL;
bpbptr,nbpbptr : frameptr;
nframe1 : frame;
PROCEDURE MoveMemory ( From, To : ADDRESS ; Bytes : LONGCARD ) ;
(* This routine shows how time critical portions of code can be optimised to
run faster. It relys on the code generation rules of the compiler which
can be checked by dis-assembling the link file with DecLnk.*)
CONST
MOVEB = 12D8H ; (* MOVE.B (A0)+,(A1)+ *)
MOVEL = 22D8H ; (* MOVE.L (A0)+,(A1)+ *)
A0 = 0+8 ; (* register A0 *)
A1 = 1+8 ; (* register A1 *)
BEGIN
SETREG(A0,From) ; (* load From pointer into A0 *)
SETREG(A1,To) ; (* load To pointer into A1 *)
IF ( ODD(From) OR ODD(To) ) THEN (* must do bytes *)
WHILE ( Bytes <> 0 ) DO
CODE(MOVEB) ;
DEC(Bytes) ;
END ;
ELSE (* even addresses so can do long moves *)
WHILE ( Bytes > 3 ) DO
CODE(MOVEL) ;
DEC(Bytes,4) ;
END ;
WHILE ( Bytes <> 0 ) DO
CODE(MOVEB) ; (* clean up remainder *)
DEC(Bytes) ;
END ;
END ;
END MoveMemory ;
(* The following procedures mimic the disk handling routines called by the
BIOS. Their procedure declarations have been written to mimic the "C"
calling sequence. *)
PROCEDURE RDRWAbs ( device, RecordNum, SectorCount : CARDINAL ;
Buffer : ADDRESS ; Flag : CARDINAL ) ;
(* NB. It is assumed that GEMDOS wont call this routine with out of range
parameters *)
CONST D0 = 0 ;
BEGIN
CODE(MOVEMDEC,SAVEREGS) ; (* save registers on stack *)
status := 0;
IF (device=Memdrive) AND (RecordNum=0) THEN (* get network stats *)
statptr:=Buffer;
statptr^:=S;
CODE(MOVEMINC,RESTREGS) ; (* Restore registers from stack *)
RETURN;
END;
IF device IN NetBits THEN (* is NETWORK channel *)
IF ( Flag = 0 ) OR ( Flag = 2 ) (* read *) THEN
FOR wsector:=0 TO (SectorCount-1) DO
C.remotedrive:=NetMap[device].Remote;
nframe1.d0:=LONGCARD(NetMap[device].Remote);
nframe1.recno:=RecordNum+wsector;
nframe1.rw:=Flag; (* read *)
resetnewdisk;
IF getfromremote(rdrwabsreq,rdrwabsconf,nframe1,local) THEN
MoveMemory(ADR(nframe1.info),Buffer+ADDRESS(wsector)*512,
512);
status:=0;
ELSE
status:=(-11);
END; (* if *)
END; (* for *)
IF networkerror THEN C.sendreset:=TRUE END; (* send network reset to remote cpu *)
SETREG(D0,status) ;
ELSIF ( Flag = 1 ) OR ( Flag = 3 ) THEN (* write *)
IF NetMap[device].Write THEN
FOR wsector:=0 TO (SectorCount-1) DO
C.remotedrive:=NetMap[device].Remote;
nframe1.d0:=LONGCARD(NetMap[device].Remote);
nframe1.recno:=RecordNum+wsector;
nframe1.rw:=Flag; (* write *)
resetnewdisk;
MoveMemory(Buffer+ADDRESS(wsector)*512,ADR(nframe1.info),512);
IF getfromremote(rdrwabsreq,rdrwabsconf,nframe1,local) THEN
status:=0;
ELSE
status:=(-10);
END;
END; (* for *)
IF networkerror THEN C.sendreset:=TRUE END; (* send network reset to remote cpu *)
ELSE
status:=(-13); (* write protect *)
END;
SETREG(D0,status) ;
ELSE
SETREG(D0,LONGINT(-3)) ;
END ;
ELSE (* not NETWORK *)
rwabsLock:=TRUE;
SaveHDRWAbs (device,RecordNum,SectorCount,Buffer,Flag) ;
rwabsLock:=FALSE;
END ;
CODE(MOVEMINC,RESTREGS) ; (* Restore registers from stack *)
END RDRWAbs ;
PROCEDURE RDMediaCh ( device : CARDINAL ) ;
CONST D0 = 0 ;
BEGIN
CODE(MOVEMDEC,SAVEREGS) ; (* save registers on stack *)
IF device IN NetBits THEN (* is NETWORK channel *)
C.remotedrive:=NetMap[device].Remote;
nframe1.d0:=LONGCARD(NetMap[device].Remote);
IF newdisk() THEN
gotmediach[NetMap[device].Remote]:=FALSE;
gotbpb[NetMap[device].Remote]:=FALSE;
END;
IF (NOT gotmediach[NetMap[device].Remote]) THEN
IF getfromremote(rdmediareq,rdmediaconf,nframe1,local) THEN
gotmediach[NetMap[device].Remote]:=TRUE;
IF nframe1.d0=1 THEN nframe1.d0:=2 END;
SETREG(D0,nframe1.d0) ; (* "C" uses D0 as return location *)
ELSE
SETREG(D0,Changed);
END;
ELSE
SETREG(D0,NoChange) ; (* "C" uses D0 as return location *)
END;
ELSE (* not NETWORK *)
rwabsLock:=TRUE;
SaveHDMediaCh(device) ;
rwabsLock:=FALSE;
END;
CODE(MOVEMINC,RESTREGS) ; (* Restore registers from stack *)
END RDMediaCh ;
PROCEDURE RDBPB ( device : CARDINAL ) ;
CONST D0 = 0 ;
BEGIN
CODE(MOVEMDEC,SAVEREGS) ; (* save registers on stack *)
IF device IN NetBits THEN (* is NETWORK channel *)
C.remotedrive:=NetMap[device].Remote;
nframe1.d0:=LONGCARD(NetMap[device].Remote);
IF newdisk() THEN
gotbpb[NetMap[device].Remote]:=FALSE;
gotmediach[NetMap[device].Remote]:=FALSE;
END;
IF (NOT gotbpb[NetMap[device].Remote]) THEN
IF getfromremote(rdbpbreq,rdbpbconf,nframe1,local) THEN
gotbpb[NetMap[device].Remote]:=TRUE;
bpbptr:=ADR(nframe1.info);
nbpbptr:=ADR(NetworkBPB[NetMap[device].Remote]);
FOR i3:=0 TO TSIZE(BPB)-1 DO
nbpbptr^[i3]:=bpbptr^[i3];
END;
resetnewdisk;
SETREG(D0,ADR(NetworkBPB[NetMap[device].Remote])); (* D0 returns address of the BPB *)
ELSE
SETREG(D0,0);
END;
ELSE
SETREG(D0,ADR(NetworkBPB[NetMap[device].Remote])); (* D0 returns address of the BPB *)
END;
IF networkerror THEN C.sendreset:=TRUE END; (* send network reset to remote cpu *)
ELSE (* not NETWORK *)
rwabsLock:=TRUE;
SaveHDBPB(device) ;
rwabsLock:=FALSE;
END ;
CODE(MOVEMINC,RESTREGS) ; (* Restore registers from stack *)
END RDBPB ;
BEGIN
END NETBIOS;
MODULE TIME;
IMPORT hz200,CODE,RTS,SuperExec,SETREG,REGISTER,ChannelLock,channel,
chanwait;
EXPORT StartTimer,TimeOut,timer,resetnewdisk,newdisk,freechannel,
resetchannel;
VAR timer : BOOLEAN;
clock : LONGCARD;
timestart,timefortimeout,timeouttime,
timestart2,timefortimeout2,timeouttime2,
timestart1,timefortimeout1,timeouttime1 : LONGCARD;
(*$P- *)
PROCEDURE gettime;
BEGIN
clock:=hz200 DIV 200;
CODE(RTS);
END gettime;
(*$P+ *)
PROCEDURE resetnewdisk;
BEGIN
SuperExec(gettime);
timestart1:=clock;
timefortimeout1:=timestart1;
IncTime(timefortimeout1,2);
END resetnewdisk;
PROCEDURE newdisk(): BOOLEAN;
BEGIN
SuperExec(gettime);
timeouttime1:=clock;
SETREG(0,timeouttime1);
CODE(0280H,0,0FFFFH);
timeouttime1:=LONGCARD(REGISTER(0));
IF timeouttime1>timefortimeout1 THEN
resetnewdisk;
RETURN TRUE;
END;
RETURN FALSE;
END newdisk;
PROCEDURE resetchannel;
BEGIN
SuperExec(gettime);
timestart2:=clock;
timefortimeout2:=timestart2;
IncTime(timefortimeout2,chanwait);
END resetchannel;
PROCEDURE freechannel(): BOOLEAN;
BEGIN
SuperExec(gettime);
timeouttime2:=clock;
SETREG(0,timeouttime2);
CODE(0280H,0,0FFFFH);
timeouttime2:=LONGCARD(REGISTER(0));
IF timeouttime2>timefortimeout2 THEN
resetchannel;
ChannelLock:=none;
RETURN TRUE;
END;
RETURN FALSE;
END freechannel;
PROCEDURE StartTimer;
BEGIN
SuperExec(gettime);
timestart:=clock; (* set to time in seconds *)
timer:=TRUE;
timefortimeout:=timestart;
IncTime(timefortimeout,5);
END StartTimer;
PROCEDURE IncTime(VAR t : LONGCARD; c: CARDINAL);
BEGIN
IF c<1 THEN RETURN END;
t:=t+LONGCARD(c);
END IncTime;
PROCEDURE TimeOut(): BOOLEAN;
BEGIN
IF (NOT timer) THEN RETURN FALSE END;
SuperExec(gettime);
timeouttime:=clock;
SETREG(0,timeouttime);
CODE(0280H,0,0FFFFH);
timeouttime:=LONGCARD(REGISTER(0));
IF timeouttime>timefortimeout THEN
StartTimer;
RETURN TRUE;
END;
RETURN FALSE;
END TimeOut;
BEGIN
END TIME;
MODULE EVENT; (* local module *)
IMPORT C,evtype,R,S,trace,framebufferfull,BConOut,Device,getf,message,
rframe,RFRAME,vblLock,FlushBuffer,framewaiting,recframesize,
rframeptr,requesttosend,cleartosend,TimeOut,frame,FrameKind,
DataKind,MediaChange,GetBPB,ADR,ADDRESS,sendtoremote,event,
ScreenPhysicalBase,TSIZE,BPB,Memdrive,RWAbs,RW,frameptr,timer,
kbdiorec,IORec,SerialDevice,statptr,GetDateTime,senddata,sendf,
gotframe,charcount,Maxdrives,gotmediach,gotbpb,SFRAME,debug,
inc,StartTimer,NETTIME,TIMESET,BEL,getfromremote,SetTime,
SetDate,OneTime,ChannelLock,channel;
EXPORT Nwait,ToHost,HandleEvents,RESET;
VAR nframe2 : frame;
d : CARDINAL;
PROCEDURE Nwait(VAR e: evtype);
VAR i2,cksum : CARDINAL;
BEGIN
IF C.sendreset THEN
e:=reset;
INC(S.resets);
RETURN;
END;
IF framebufferfull THEN
IF trace THEN BConOut(CON,"k") END;
cksum:=0;
FOR i2:=0 TO recframesize-5 DO
cksum:=cksum+CARDINAL(rframeptr^[i2])
END;
IF (cksum=rframe.cksum) THEN
getf(RFRAME);
e:=framearrival;
INC(R);
IF trace THEN BConOut(CON,"u") END;
RETURN;
ELSE
e:=nothing; (* checksum error *)
framebufferfull:=FALSE;
FlushBuffer();
INC(S.retrys);
INC(S.checksumerrors);
IF trace THEN BConOut(CON,"U") END;
END;
RETURN;
END;
IF requesttosend AND cleartosend THEN
e:=hostready;
RETURN;
END;
IF TimeOut() THEN
e:=timeout;
INC(R);
INC(S.retrys);
INC(S.timeouts);
END; (* so sorry no frame ack *)
END Nwait;
PROCEDURE ToHost(VAR f: frame);
VAR i,r : INTEGER;
d : CARDINAL;
bpbptr,nbpbptr : frameptr;
meminfo : POINTER TO message;
screen1 : POINTER TO ARRAY [0..255]
OF CARDINAL;
ibuf,bbuf : POINTER TO ARRAY
[0..32] OF LONGCARD;
BEGIN
IF trace THEN BConOut(CON,"H") END;
IF f.kind=callreq THEN
RETURN;
END;
IF f.kind=clearreq THEN
RETURN;
END;
IF f.kind=diag THEN
RETURN;
END;
IF f.kind=data THEN
IF f.cmd=rdmediareq THEN
IF trace THEN BConOut(CON,"M") END;
nframe2.d0:=LONGCARD(MediaChange(CARDINAL(f.d0)));
sendtoremote(data,rdmediaconf,nframe2,remote);
RETURN;
END;
IF f.cmd=rdbpbreq THEN
IF trace THEN BConOut(CON,"P") END;
nframe2.d0:=LONGCARD(GetBPB(CARDINAL(f.d0)));
bpbptr:=ADDRESS(nframe2.d0);
nbpbptr:=ADR(nframe2.info);
FOR i:=0 TO TSIZE(BPB)-1 DO
nbpbptr^[i]:=bpbptr^[i];
END;
sendtoremote(data,rdbpbconf,nframe2,remote);
RETURN;
END;
IF f.cmd=rdrwabsreq THEN
IF trace THEN BConOut(CON,"W") END;
INC(S.rwabsreqs);
IF f.d0#Memdrive THEN
nframe2.d0:=LONGCARD(RWAbs(RW(f.rw),ADR(f.info),1,f.recno,
CARDINAL(f.d0)));
END;
IF (f.d0=Memdrive) AND (f.recno>3) THEN
IF trace THEN BConOut(CON,"V") END;
nframe2.d0:=0;
meminfo:=ADDRESS(LONGCARD(f.recno)*LONGCARD(512));
IF (f.rw=0) OR (f.rw=2) THEN (* read *)
f.info:=meminfo^;
ELSE
meminfo^:=f.info; (* write *)
END;
END;
IF (f.d0=Memdrive) AND (f.recno=3) THEN
nframe2.d0:=0;
meminfo:=ScreenPhysicalBase();
screen1:=ADR(f.info);
FOR i:=0 TO 63 DO
screen1^[i]:=0;
FOR r:=0 TO 511 DO
screen1^[i]:=screen1^[i]+CARDINAL(meminfo^[0]);
meminfo:=ADDRESS(LONGCARD(meminfo)+LONGCARD(1));
END;
END;
END;
IF (f.d0=Memdrive) AND (f.recno=2) THEN (* remote ikbd *)
nframe2.d0:=0;
kbdiorec:=IORec(Keyboard); (* length in info[0] *)
ibuf:=kbdiorec^.ibuf;
bbuf:=ADR(f.info);
kbdiorec^.ibufhd:=0;
kbdiorec^.ibuftl:=0;
FOR i:=1 TO INTEGER(bbuf^[0]) DO
ibuf^[i]:=bbuf^[i];
END;
kbdiorec^.ibufhd:=0;
kbdiorec^.ibuftl:=CARDINAL(bbuf^[0]*4);
END;
IF (f.d0=Memdrive) AND (f.recno=1) THEN
statptr:=ADR(f.info); (* load remote stats *)
statptr^:=S;
END;
IF (f.rw=0) OR (f.rw=2) THEN (* load read buffer *)
nframe2.rw:=f.rw;
nframe2.info:=f.info; (* if rec get buffer to send *)
END;
sendtoremote(data,rdrwabsconf,nframe2,remote);
RETURN;
END;
IF f.cmd=timereq THEN
IF trace THEN BConOut(CON,"c") END;
nframe2.d0:=GetDateTime();
sendtoremote(data,timeconf,nframe2,remote);
RETURN;
END;
END;
END ToHost;
PROCEDURE HandleEvents(VAR event: evtype);
BEGIN
IF event=nothing THEN RETURN END;
IF event=hostready THEN
event:=nothing;
IF trace THEN BConOut(CON,"S") END;
vblLock:=TRUE;
senddata;
requesttosend:=FALSE;
cleartosend:=FALSE;
END;
IF event=reset THEN
event:=nothing;
IF trace THEN BConOut(CON,"I") END;
RESET;
SFRAME.kind:=resetreq;
senddata;
IF NETTIME AND (NOT TIMESET) THEN
NetTime;
TIMESET:=TRUE;
END;
END;
IF event=framearrival THEN
event:=nothing;
IF trace THEN BConOut(CON,"F") END;
IF (RFRAME.ack=C.nextframetosend) OR debug THEN
IF trace THEN BConOut(CON,"K") END;
cleartosend:=TRUE;
StartTimer;
R:=0;
timer:=FALSE;
inc(C.nextframetosend);
END;
IF (RFRAME.seq=C.frameexpected) OR debug THEN
event:=nothing;
IF trace THEN BConOut(CON,"E") END;
IF RFRAME.kind#ack THEN (* try to exec command *)
inc(C.frameexpected);
framewaiting:=TRUE;
R:=0;
framebufferfull:=FALSE;
ToHost(RFRAME);
END;
END;
IF RFRAME.kind=resetreq THEN
event:=nothing;
IF trace THEN BConOut(CON,"*") END;
RESET;
BConOut(CON,BEL);
END;
event:=nothing;
END;
IF event=timeout THEN
event:=nothing;
IF trace THEN BConOut(CON,"R") END;
sendf(SFRAME);
END;
END HandleEvents;
PROCEDURE NetTime;
VAR nettime : ARRAY [0..1] OF CARDINAL;
timeptr : POINTER TO LONGCARD;
BEGIN
OneTime:=TRUE;
IF getfromremote(timereq,timeconf,nframe2,local) THEN
IF trace THEN BConOut(CON,"#") END;
timeptr:=ADR(nettime[0]);
timeptr^:=nframe2.d0;
SetTime(nettime[1]);
SetDate(nettime[0]);
ELSE
BConOut(CON,BEL);
event:=reset;
END;
OneTime:=FALSE;
END NetTime;
PROCEDURE RESET;
BEGIN
charcount:=0;
R:=0;
gotframe:=FALSE;
framebufferfull:=FALSE;
C.nextframetosend:=0;
C.frameexpected:=0;
FOR d:=0 TO Maxdrives DO
gotmediach[d]:=FALSE;
gotbpb[d]:=FALSE;
END;
cleartosend:=TRUE;
requesttosend:=FALSE;
framewaiting:=FALSE;
timer:=FALSE;
C.sendreset:=FALSE;
C.networkactive:=TRUE;
vblLock:=FALSE;
ChannelLock:=none;
END RESET;
BEGIN
END EVENT; (* local module *)
(* ----------------------------------------------------------------------- *)
PROCEDURE Initialise (port: Device) : BOOLEAN ;
(* returns TRUE if NETWORK is to be installed *)
BEGIN
CODE(3f3cH,0017H,4e4eH,548fH); (* gettime *)
CODE(2f00H,3f3cH,0016H,4e4eH,5c8fH); (* settime *)
IF NOT installed THEN
SuperExec(PROC(setcontrol)); (* set address of global control record *)
END;
IF port=HSS THEN
rbptr:=IORec(MIDI);
ELSE
rbptr:=IORec(RS232);
END;
rbptr^.ibuf:=ADR(rbuffer);
rbptr^.ibufsize:=2048;
rbptr^.ibufhd:=0;
rbptr^.ibuftl:=0;
C.magic:=MAGIC;
C.remotedrive:=0;
framesize:=TSIZE(frame);
recframesize:=framesize;
sndframesize:=framesize;
R:=0;
RETURN TRUE;
END Initialise ;
(* The following compiler directive stops the compiler from generating the
normal Modula-2 entry/exit code for the next procedure. This is needed as
this routine is called in supervisor mode by the BIOS function to install
the BIOS vectors. *)
(*$P- Stop entry/exit code for next procedure *)
PROCEDURE InstallVectors ;
BEGIN
(* First save the current hard disk vectors *)
SaveHDBPB := CBPBProc(HDBPB) ;
SaveHDRWAbs := CRWAbsProc(HDRWAbs) ;
SaveHDMediaCh := CMediaChProc(HDMediaCh) ;
SaveCritic := PROC(EvtCritic);
(* Now set the BIOS vectors to our routines *)
HDBPB := ADDRESS(RDBPB) ;
HDRWAbs := ADDRESS(RDRWAbs) ;
HDMediaCh := ADDRESS(RDMediaCh) ;
EvtCritic := ADDRESS(NetCritic);
drvnr:=2; (* start from drive C *)
WHILE drvnr IN DriveBits DO
INC(drvnr);
END; (* while *)
INC(drvnr); (* start of network drives *)
R := 0; (* remote = A *)
GetOpt;
Open("MX2NET.INF",0,handle);
IF handle>0 THEN
count:=128;
GEMDOS.Read(handle,count,ADR(NetInfo));
OK:=Close(handle);
ELSE
count:=0;
END;
OK:=FALSE; (* set to READONLY *)
IF count>0 THEN
FOR d := 0 TO CARDINAL(count) BY 4 DO
IF d<CARDINAL(count) THEN
R := CARDINAL(BITSET(NetInfo[0+d]) * BITSET(31) )-1;
drvnr := CARDINAL(BITSET(NetInfo[1+d]) * BITSET(31) )-1;
IF (R>Maxdrives) OR (drvnr>Maxdrives) THEN R:=0; drvnr:=0; END;
IF (NetInfo[2+d]='W') OR (NetInfo[2+d]='w') THEN
OK := TRUE;
ELSE
OK := FALSE;
END;
NetMap[drvnr].Remote := R;
NetMap[drvnr].Local := drvnr;
NetMap[drvnr].Write := OK;
IF ((NOT (drvnr IN DriveBits)) OR NETMASK) AND (drvnr>1) THEN
INCL(DriveBits,NetMap[drvnr].Local);
INCL(NetBits, NetMap[drvnr].Local);
END;
END;
END;
END;
R:=0;
d:=0;
networkconnect := FALSE;
gotframe := FALSE;
framebufferfull := FALSE;
charcount:=0;
SIZEframe:=TSIZE(frame);
SIZEframecmd:=TSIZE(framecmd);
rframeptr := ADR(rframe);
framecmdptr:=ADR(rframe);
CODE(RTS) ; (* code to return to calling BIOS function *)
END InstallVectors ;
(*$P+ *)
PROCEDURE GetOpt;
VAR d : CARDINAL;
BEGIN
Open("MX2NET.OPT",0,handle);
IF handle>0 THEN
count:=128;
GEMDOS.Read(handle,count,ADR(NetInfo));
OK:=Close(handle);
ELSE
count:=0;
END;
NETTIME:=FALSE;
NETMASK:=FALSE;
MEMMASK:=FALSE;
PHYSLOW:=FALSE;
IF count>0 THEN
FOR d:=0 TO CARDINAL(count) DO
IF NetInfo[d]='t' THEN (* get gemdos time *)
NETTIME:=TRUE;
END;
IF NetInfo[d]='o' THEN (* over-write existing drive map *)
NETMASK:=TRUE;
END;
IF NetInfo[d]='m' THEN (* memory reads-write useing rwabs *)
MEMMASK:=TRUE;
NetMap[Memdrive].Remote := Memdrive;
NetMap[Memdrive].Local := Memdrive;
NetMap[Memdrive].Write := TRUE;
INCL(NetBits, Memdrive);
END;
IF NetInfo[d]='5' THEN (* remote is 520 ST, use low ram screen *)
PHYSLOW:=TRUE;
END;
END;
END;
END GetOpt;
(*$P- *) (* set vector to control record *)
PROCEDURE setcontrol;
BEGIN
IF Mptr#MAGIC THEN
C.drivemap:=DriveMap();
Dptr:=C.drivemap;
END;
C.drivemap:=Dptr;
Mptr:=MAGIC;
CODE(RTS);
END setcontrol;
(*$P+ *)
PROCEDURE FlushBuffer();
BEGIN
rbptr^.ibufhd:=0;
rbptr^.ibuftl:=0;
END FlushBuffer;
PROCEDURE nrecframe;
BEGIN
vblLock:=TRUE;
IF C.networkactive THEN
WHILE (BConStat(netdevice)) AND (NOT framebufferfull) DO
recchar := BConIn(netdevice);
IF (NOT gotframe) AND (CHAR(recchar)=SYN) THEN
gotframe:=TRUE; (* got sync char from data *)
charcount:=0;
END;
IF (charcount=1) AND ((CHAR(recchar)#STX)
AND (CHAR(recchar)#SOH)) THEN
gotframe:=FALSE; (* false start try again *)
charcount:=0;
END;
IF (charcount=1) AND (CHAR(recchar)=STX) THEN
recframesize:=SIZEframe;
END;
IF (charcount=1) AND (CHAR(recchar)=SOH) THEN
recframesize:=SIZEframecmd;
END;
IF gotframe THEN (* put data in buffer *)
rframeptr^[charcount]:=BYTE(recchar);
INC(charcount);
IF charcount=recframesize THEN (* got full frame *)
gotframe := FALSE;
IF trace THEN BConOut(CON,"^") END;
IF recframesize=SIZEframecmd THEN
rframe.cksum:=framecmdptr^.cksum;
END;
framebufferfull := TRUE;
RETURN;
END;
END;
END; (* WHILE *)
END;
END nrecframe;
PROCEDURE getf(VAR f: frame);
BEGIN
INC(S.inpackets);
f:=rframe;
framebufferfull:=FALSE;
END getf;
PROCEDURE senddata;
BEGIN
vblLock:=TRUE;
SFRAME.seq:=C.nextframetosend;
SFRAME.ack:=1-C.frameexpected;
sendf(SFRAME);
IF (SFRAME.kind#ack) AND (SFRAME.kind#resetreq) THEN
StartTimer; (* set timer to wait for frame ack from remote host *)
END;
END senddata;
PROCEDURE sendf(VAR f: frame);
BEGIN
vblLock:=TRUE;
INC(S.outpackets);
sframeptr := ADR(sframe);
sframe:=f;
sframe.cksum:=0;
IF ((sframe.cmd=rdrwabsconf) AND ((sframe.rw=0)
OR (sframe.rw=2))) OR ((sframe.cmd=rdrwabsreq)
AND ((sframe.rw=1) OR (sframe.rw=3))) THEN
sndframesize:=SIZEframe;
sframe.syn := SYN ;
sframe.stx := STX ;
shortframe:=FALSE;
IF trace THEN BConOut(CON,":") END;
ELSE
sndframesize:=SIZEframecmd;
sframe.syn := SYN ;
sframe.stx := SOH ;
sframecmdptr:=ADR(sframe);
shortframe:=TRUE;
IF trace THEN BConOut(CON,".") END;
END;
FOR i1:=0 TO sndframesize-5 DO (* compute checksum *)
sframe.cksum:=sframe.cksum+CARDINAL(sframeptr^[i1])
END;
IF shortframe THEN sframecmdptr^.cksum:=sframe.cksum END;
FOR i1:=0 TO sndframesize-1 DO (* send frame *)
BConOut(netdevice,CHAR(sframeptr^[i1]));
END;
END sendf;
PROCEDURE waitcts(what: BOOLEAN); (* wait for cleartosend state *)
BEGIN
IF what THEN
IF trace THEN BConOut(CON,"+") END;
IF OneTime THEN R:=retry END;
REPEAT
nrecframe;
Nwait(event);
HandleEvents(event);
IF R>retry THEN
networkerror:=TRUE;
RETURN; (* trouble *)
END;
UNTIL cleartosend;
ELSE
IF trace THEN BConOut(CON,"-") END;
Nwait(event);
HandleEvents(event);
END;
IF trace THEN BConOut(CON,"N") END;
END waitcts;
PROCEDURE WaitChannel(chan: channel);
BEGIN
IF trace AND (chan=local) THEN BConOut(CON,"<") END;
IF trace AND (chan=remote) THEN BConOut(CON,">") END;
IF trace AND (chan=none) THEN BConOut(CON,"|") END;
IF (ChannelLock=none) OR (ChannelLock=chan) THEN
ChannelLock:=chan;
resetchannel;
RETURN;
END;
IF trace THEN BConOut(CON,"!") END;
REPEAT
nrecframe;
Nwait(event);
HandleEvents(event);
UNTIL freechannel();
ChannelLock:=chan;
END WaitChannel;
(* request for data from remote hosts disk drives and system *)
(* what wanted in command, the correct reply in reply, data in f *)
PROCEDURE getfromremote(command, reply: DataKind; VAR f: frame;
chan: channel): BOOLEAN;
VAR ticks : CARDINAL;
BEGIN
IF (NOT C.networkactive) THEN RETURN FALSE END; (* error *)
WaitChannel(chan);
vblLock:=TRUE;
networkerror:=FALSE;
R:=0;
StartTimer;
IF trace THEN BConOut(CON,"A") END;
f.kind:=data;
f.cmd:=command;
waitcts(TRUE);
IF networkerror THEN
vblLock:=FALSE;
RETURN FALSE;
END;
IF trace THEN BConOut(CON,"B") END;
SFRAME:=f;
framewaiting:=FALSE;
requesttosend:=TRUE;
waitcts(FALSE);
REPEAT
UNTIL (NOT requesttosend);
IF networkerror THEN
vblLock:=FALSE;
RETURN FALSE;
END;
IF trace THEN BConOut(CON,"C") END;
ticks:=0;
IF OneTime THEN R:=retry END;
REPEAT
INC(ticks);
nrecframe;
Nwait(event);
HandleEvents(event);
IF ticks>64000 THEN networkerror := TRUE END;
IF R>retry THEN networkerror:=TRUE END;
IF networkerror THEN
vblLock:=FALSE;
RETURN FALSE;
END;
UNTIL framewaiting AND (RFRAME.cmd=reply);
IF trace THEN BConOut(CON,"D") END;
f:=RFRAME;
f.rw:=5;
f.kind:=ack;
f.cmd:=reply;
sendf(f); (* send ack for reply *)
IF networkerror THEN
vblLock:=FALSE;
RETURN FALSE;
END;
IF trace THEN BConOut(CON,"Z") END;
vblLock:=FALSE;
RETURN TRUE;
END getfromremote;
PROCEDURE sendtoremote(type: FrameKind; command: DataKind;VAR f: frame;
chan: channel);
BEGIN
WaitChannel(chan);
vblLock:=TRUE;
IF trace THEN BConOut(CON,"T") END;
f.kind:=type;
f.cmd:=command;
IF debug THEN cleartosend:=TRUE END; (* so we can send in loop *)
waitcts(TRUE);
IF trace THEN BConOut(CON,"1") END;
SFRAME:=f;
requesttosend:=TRUE;
waitcts(FALSE);
IF trace THEN BConOut(CON,"2") END;
IF SFRAME.kind=ack THEN cleartosend:=TRUE END;
vblLock:=FALSE;
END sendtoremote;
(*$P- *)
PROCEDURE NetCritic;
BEGIN
CODE(RTS);
END NetCritic;
(*$P+ *)
PROCEDURE recframe;
BEGIN
EvtCritic := ADDRESS(NetCritic);
IF (NOT vblLock) AND (NOT rwabsLock) THEN
vblLock:=TRUE;
nrecframe;
Nwait(event);
HandleEvents(event);
vblLock:=FALSE;
END;
END recframe;
PROCEDURE NoCritrecframe;
BEGIN
IF (NOT vblLock) AND (NOT rwabsLock) THEN
vblLock:=TRUE;
nrecframe;
Nwait(event);
HandleEvents(event);
vblLock:=FALSE;
END;
END NoCritrecframe;
PROCEDURE initnetwork(port: Device);
VAR d : CARDINAL;
BEGIN
netdevice:=port;
IF Initialise(port) THEN
RESET;
rwabsLock:=FALSE;
IF NOT installed THEN
SuperExec(PROC(InstallVectors)) ; (* install the NETWORK *)
installed:=TRUE;
END;
event:=reset;
END ;
END initnetwork;
PROCEDURE networkoff;
BEGIN
C.networkactive:=FALSE;
END networkoff;
PROCEDURE networkon;
BEGIN
C.networkactive:=TRUE;
END networkon;
BEGIN
BConOut(CON,ESC);
BConOut(CON,'E');
GEMDOS.ConWS(TITLE);
BConOut(CON,CR);
BConOut(CON,LF);
END NETWORK.